﻿using System;
using System.Collections.Generic;
using System.Text;
using Extensibility;
using Microsoft.VisualStudio.CommandBars;
using EnvDTE;
using EnvDTE80;
using VSAddinLibrary.Controllers;
using VSAddinLibrary.Utils;
using System.Configuration;
using VSALC = VSAddinLibrary.Config;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using VSAddinLibrary.Config;
using VSAddinLibrary.Attributes;



namespace VSAddinLibrary
{
  [ComVisible(true)]
  public abstract class BaseConnect : IDTExtensibility2, IDTCommandTarget
  {
    AddIn _addInInstance;
    public AddIn AddInInstance
    {
      get { return _addInInstance; }
    }

    DTE2 _applicationObject;
    public DTE2 ApplicationObject
    {
      get { return _applicationObject; }
    }

    #region Constructor
    public BaseConnect()
    {

    }
    #endregion

    #region Public Methods
    public virtual void OnCreateCommands()
    {

    }
    #endregion Public Methods

    #region Private Methods

    #endregion Private Methods

    #region IDTExtensibility2 Members

    public virtual void OnAddInsUpdate(ref Array custom)
    {

    }

    public virtual void OnBeginShutdown(ref Array custom)
    {

    }

    public virtual void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
    {
      try
      {
        _applicationObject = (DTE2) Application;
        _addInInstance = (AddIn) AddInInst;
        if (ConnectMode == ext_ConnectMode.ext_cm_UISetup)
        {
          OnCreateCommands();

          Type type = this.GetType();
          // load vscommands by reflection
          if (FlagAttribute.GetFlagValue<LoadVSCommandReflectionAttribute>(type))
          {
            LoadVSCommandReflection();
          }

          // load UI setup configuration
          if (FlagAttribute.GetFlagValue<UISetupConfigurationAttribute>(type))
          {
            VSALC.VSAddinConfigurationSection configSection = LoadConfiguration() as VSALC.VSAddinConfigurationSection;
            if (configSection != null)
            {
              UISetup(configSection);
            }
          }
        }
        else if (ConnectMode == ext_ConnectMode.ext_cm_Startup)
        {

        }
      }
      catch(Exception ex)
      {
        writeLog(ex.Message);
      }
    }


    public virtual void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
    {

    }

    public virtual void OnStartupComplete(ref Array custom)
    {

    }

    #endregion

    #region IDTCommandTarget Members

    public virtual void Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled)
    {
      try
      {
        IVSCommand command = VSCommandManager.GetCommandByName(CmdName);
        if (command != null)
        {
          command.Exec(ExecuteOption, ref VariantIn, ref VariantOut, ref Handled);
        }
      }
      catch(Exception ex)
      {
        writeLog(ex.Message);
      }
    }

    public virtual void QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText)
    {
      try
      {
        IVSCommand command = VSCommandManager.GetCommandByName(CmdName);
        if (command != null)
        {
          command.QueryStatus(NeededText, ref StatusOption, ref CommandText);
        }
      }
      catch(Exception ex)
      {
        writeLog(ex.Message);
      }
    }

    #endregion

    #region Public Methods
    public void LoadVSCommandReflection()
    {
      List<Type> lstVSCommands = AddinReflector.GetVSCommandTypes(this.GetType().Assembly);
      foreach (Type type in lstVSCommands)
      {
        try
        {
          object command = Activator.CreateInstance(type);
          IVSCommand vsCommand = command as IVSCommand;
          Command cmd = createCommand(vsCommand.CommandName, vsCommand.Caption, vsCommand.ToolTip);
          if (cmd != null)
          {
            vsCommand.Command = cmd;
            VSCommandManager.AddCommand(vsCommand.CommandName, vsCommand);
          }
        }
        catch(Exception ex)
        {
          writeLog(ex.Message);
         
        }
      }

    }

    public VSALC.VSAddinConfigurationSection LoadConfiguration()
    {
      AppDomain domain = AppDomain.CurrentDomain;
      // needed for OpenMappedExeConfiguration can properly resolve the assembly
      domain.AssemblyResolve += new ResolveEventHandler(domain_AssemblyResolve);
      VSALC.VSAddinConfigurationSection vsAddinConfigSection = null;
      try
      {
        ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
        string configfilePath = getConfigfilePath();
        fileMap.ExeConfigFilename = configfilePath;
        // get configuration
        System.Configuration.Configuration config =
          System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        vsAddinConfigSection =
          config.Sections["vSAddinConfigurationSection"] as VSALC.VSAddinConfigurationSection;
      }
      catch (ConfigurationErrorsException ex)
      {
        writeLog(ex.Message);
      }
      return vsAddinConfigSection;
    }


    #endregion Public Methods

    #region Private Methods

    private string getConfigfilePath()
    {
      Assembly addinAssembly = Assembly.GetAssembly(this.GetType());

      string directory = Path.GetDirectoryName(addinAssembly.Location);

      string configFileName = string.Concat(Path.GetFileName(addinAssembly.Location), ".config");
      Directory.SetCurrentDirectory(directory);
      AddinConfigurationAttribute att = AddinReflector.GetAttrib<AddinConfigurationAttribute>(this.GetType());
      if (att != null)
      {
        configFileName = att.ConfigFileName;
      }
      return Path.Combine(directory, configFileName);
    }

    Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
      Assembly result = null;
      try
      {
        AssemblyName assemblyName = new AssemblyName(args.Name);
        if (assemblyName.Name == "VSAddinLibrary")
        {
          result = Assembly.GetAssembly(typeof(VSALC.VSAddinConfigurationSection));
        }
      }
      catch (Exception ex)
      {
        writeLog(ex.Message);
      }
      return result;
    }

    private void writeLog(string message)
    {
      EventLog.WriteEntry(this.GetType().ToString(), message);
    }

    public void UISetup(VSALC.VSAddinConfigurationSection vsAddinConfigSection)
    {
      // for each command, create a VSCommand
      foreach (CommandBarData commandBarData in vsAddinConfigSection.CommandBarsData)
      {
        createCommandBar(commandBarData);
      }

      foreach (CommandBarData commandBarData in vsAddinConfigSection.ToolBarsData)
      {
        createToolBar(commandBarData);
      }


    }

    private CommandBar getCommandBar(string name)
    {
      CommandBars cmdBars = _applicationObject.CommandBars as CommandBars;
      CommandBar cmdBar = null;
      try
      {
        cmdBar = cmdBars[name];
      }
      catch (Exception ex)
      {
        writeLog(ex.Message);
      }

      return cmdBar;
    }

    private void createToolBar(CommandBarData commandBarData)
    {
      CommandBars cmdBars = _applicationObject.CommandBars as CommandBars;


      CommandBar toolBar = getCommandBar(commandBarData.Name);

      if (toolBar == null)
      {
        toolBar = (CommandBar)_applicationObject.Commands.AddCommandBar(commandBarData.Name,
                                                               vsCommandBarType.vsCommandBarTypeToolbar,
                                                               null,
                                                               1);
      }

      toolBar.Position = MsoBarPosition.msoBarTop;
      // Make the toolbar visible
      toolBar.Visible = true;
      foreach (CommandBarBtnPopupData cmdbp in commandBarData.CommandBarPopupStrip)
      {
        createCommandBarEntry(toolBar, cmdbp);
      }
    }

    private void createCommandBar(CommandBarData commandBarData)
    {
      CommandBars cmdBars = _applicationObject.CommandBars as CommandBars;

      CommandBar cmdBar = cmdBars[commandBarData.Name];

      foreach (CommandBarBtnPopupData cmdbp in commandBarData.CommandBarPopupStrip)
      {
        createCommandBarEntry(cmdBar, cmdbp);
      }
    }

    private void createCommandBarEntry(CommandBar commandBar, CommandBarBtnPopupData commandBarBtnPopupData)
    {
      if (string.IsNullOrEmpty(commandBarBtnPopupData.CommandName))
      {
        // Add a new commandbar popup 
        CommandBarPopup commandBarPopup = commandBar.Controls.Add(
                                            MsoControlType.msoControlPopup, System.Type.Missing, System.Type.Missing,
                                            commandBar.Controls.Count + 1, true) as CommandBarPopup;
        if (commandBarPopup != null)
        {
          commandBarPopup.Caption = commandBarBtnPopupData.Caption;

          foreach (CommandBarBtnPopupData commandBarBtnPpData in commandBarBtnPopupData.Children)
          {
            createCommandBarEntry(commandBarPopup.CommandBar, commandBarBtnPpData);
          }
        }
      }
      else
      {
        createCommandButton(commandBar, commandBarBtnPopupData);
      }
    }

    private void createCommandButton(CommandBar commandBar, CommandBarBtnPopupData cb)
    {
      try
      {
        string commandName = cb.CommandName;
        string caption = cb.Caption;
        string tooltip = cb.ToolTip;
        Command command = createCommand(commandName, caption, tooltip);
        command.AddControl(commandBar, 1);
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
    }

    private Command createCommand(string commandName, string caption, string tooltip)
    {
      Command command = null;
      try
      {
        // Try to retrieve the command, just in case it was already created, ignoring the 
        // exception that would happen if the command was not created yet.
        try
        {
          string commandFullName = AddInInstance.ProgID + "." + commandName;
          command = ApplicationObject.Commands.Item(commandFullName, -1);
        }
        catch (Exception ex)
        {
        }

        object[] contextUIGuids = null;
        // Add the command if it does not exist
        if (command == null)
        {
          command = ApplicationObject.Commands.AddNamedCommand(AddInInstance,
                                                               commandName, caption, tooltip, true, 59,
                                                               ref contextUIGuids,
                                                               (int)
                                                               (vsCommandStatus.vsCommandStatusSupported |
                                                                vsCommandStatus.vsCommandStatusEnabled));
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message);
      }
      return command;
    }
    #endregion Private Methods
  }
}
